查看原文
其他

Unity预计算实时GI(五)UV展开与减少光照图数量

2017-02-16 Unity官方 Unity官方平台

我们曾经为大家介绍了预计算实时GI的必要性、如何定义一个适合场景的光照分辨率、光照图的概念以及使用光照探针优化光照的方案。您可以点击下面的标题对之前的教程进行回顾:


本文我们一起来看看如何帮助剩下的光照图做瘦身工作。


UV展开


为什么要讨论UV展开?因为生成光照图(Charts)是为了包裹静态网格渲染器(Static Mesh Renderer)的UV贴图坐标,所以一个对象所需要的光照图数量主要取决于物体需要展开的UV Shell数量。这就涉及到UV展开的问题,它需要保持几何面上贴图像素扭曲度与所需Shell数量之间的平衡。

 

一般来说Unity的UV展开算法能得到不错的结果,但有时候需要我们手动调整设置,因此有必要了解一下自动UV展开功能背后的原理。下面我们来看看三个UV展开的不同例子。


这样的UV展开不会变形,但需要多张UV Shell


从上图我们可以看到,作为贴图的棋盘网格图像瓦片一样保持比例地贴在立方体表面,并没有变形。试想如果这个棋盘图是一个光照贴图,我们会得到一个视觉正常没有变形的结果,只是需要耗费6个UV Shell。这就表示用Unity PRGI进行计算时也将生成六张光照图,从曾经介绍过的光照图的概念一文中我们了解到,不论何种条件,每张光照图至少都需要4x4 texel来表示,这在还没考虑分辨率之前最少就会消耗96个像素。

 

用单一UV Shell所产生的结果,贴图变形很严重


上图中物体的UV贴图坐标只用一个UV Shell就涵盖了所有范围,虽然这种做法生成的光照图最少,但视觉效果却不理想。 我们会看到物体表面的贴图歪了,而且它们在UV空间里也相互重叠,如果这是一张光照贴图,物体一面的光照可能会错误投射到相反面上。很明显这种UV展开是有问题的。


比较理想的结果,单一UV Shell且贴图未变形

 

上图的结果比较理想,棋盘没有扭曲,网格比例也保持正方形。而且还成功地用一张UV Shell覆盖了物体的所有面。届时通过连接或缝合对应在模型的边缘以匹配位置。

 

如果从程序化角度考虑,整个UV展开过程做了哪些事情呢?首先我们会通过正交投影(Orthogonal Projection)让UV贴图在物体上生成独立的Shell,然后分析哪些Shell和物体的边缘有相连关系,一旦找到这些边缘,最后把UV Shell的内容移进去并与相邻的Shell缝合起来。


光照图的可视化


在开始优化UV展开流程和光照图之前,我们需要想办法在编辑器中查看它们。光照图是在网格导入流程中的展开阶段(Unwrapping stage)生成的。对于PRGI来说,这些光照图会在预计算的几何阶段(Geometry stage)被打包到不同的图集(Atlas)里,这样做是为了确保它们不会相互重叠。一旦预计算的几何阶段完成之后就会产生可视化数据,我们就能预览光照图。

 

如果您使用版本控制(Version Control)进行管理,需要注意这个预览信息只会储存在本机,所以在开始查看之前要执行一次预计算。


UV Charts模式

 

如上图所示,UV Charts模式会将场景中不同光照图表示为不同的颜色,光照图分辨率用附在上面的棋盘格表示。在场景视图切换为UV Charts绘制模式时,可以快速查看光照图,我们可以从场景视图(Scene View)左上方的Draw Mode下拉列表中选择UV Charts。如果启用了自动烘焙(Window > Lighting > Auto),也会自动计算UV展开参数的改变并在场景视图中更新。



如上图,Charting模式的预览视图会用不同颜色的格子表示光照图,并用浅蓝色的线表示光照贴图的UV。

 

当场景较为复杂时,从场景视图中有可能会漏看光照图。这时候我们可以在Lighting视图中使用预览模式查看单个物体使用的所有光照图。这样可以帮我们更精确地评估这些物体的UV展开结果,有助于降低光照图的数量。具体操作如下:

  • 打开Lighting界面(Window > Lighting),选择Object标签页

  • 从层级视图(Hierarchy)中选择要查看的对象。

  • 查看Lighting界面的预览视图,从最上方的下拉列表中选择Charting模式。

 

对象使用的光照图数量会以不同颜色的方块叠放在对应的浅蓝色UV坐标上。


UV展开参数说明


下面我们一起来看一下几个有助于优化UV展开的设置,所有的设置都是针对单一对象,具体设置方法如下:

  • 打开Lighting界面(Window > Lighting)并选择Object标签页

  • 从层级视图(Hierarchy)中选择要设置的对象


在设置的时候,我们会遇到一些参数,下面就一起来看看。


Auto UV Max Distance(自动最大UV距离)

Unity的UV展开算法会尝试调整不同的Shell,将UV边缘拼接在一起来简化UV贴图。如果加入Shell后还能将UV尺寸保持在Auto UV Max Distance设置的范围内,才会被考虑加入。这个范围是用Unity的世界空间坐标来定义的,在我们的示例里是1米。


在Lighting视图的Object标签页下设置Auto UV Max Distance

 

大多数情况下,预设的0.5就能有很好的效果,但对于面积较大的物体可能要提高这个值来防止本来应该被缝合的UV贴图被UV展开算法排除在外

 

增加这个值通常会让物体所需的光照图数量减少,而降低这个值通常有助于解决贴图像素被拉伸的问题,当然也会需要生成更多的光照图进行覆盖。 改变这个值后可以使用UV Charts绘制模式查看场景,从覆盖的棋盘图来评估并进行实验以找出一个最好的平衡点。


Auto UV Max Angle(自动最大UV角度)

UV展开的计算当然也会考虑到相邻Shell的角度,Auto UV Max Angle定义了相邻面共享边缘允许的最大角度,并用内角来计算。如果背面的角度大于这个值,就会排除缝合这个UV Shell。

 

在Lighting视图的Object标签页下设置Auto UV Max Angle

 

提高这个值会让Unity的算法更容易组合UV图,我们能通过这个功能来降低单个对象的光照图数量,但如果设得太宽松,有时会出现贴图被拉伸的情况。反之,降低这个值会造成算法不易将相邻的UV排一起,虽然拉伸的情况会减少,但是会生成更多光照图。以同样的方式,我们可以在UV Charts绘制模式下查看棋盘图并尝试出一个最适合的值。


Preserve UVs(保留UV)

在某些情况下,自动展开UV如果无法获得理想效果,可能会导致生成过多的光照图或贴图失真(GI Charts绘制模式可以做拉伸检查)。 在这种情况下可能需要在模型的UV01通道手动创建UV。 这必须要在其它工具中完成。

 

如果出现这种情况,可以在Preserve UVs选项让Unity算法强制使用模型UV01通道指定的UV Shell


当需要手动保留UV图时,Preserve UVs选项很有用

 

要注意的是,这些Shell会被重新打包以节省光照贴图空间,它们会被单独解开保留,而非仅仅记录光照贴图内的坐标信息。

 

使用这个功能时必须小心,当指定的UV贴图包含大量的UV Shell时,这个功能可能会让预计算的时间加长,因为会跳过Unity的UV展开算法,手动保留的UV Shell到时候会全部交给预计算流程。 请注意,最好的结果是尽可能的降低UV Shell和光照贴图数量,并将UV贴图保持在可接受的拉伸范围内。


Ignore Normals(忽略法线)

在某些情况下,网格导入器可能会拆分几何体,这也会影响到光照贴图数量。例如,如果某个网格有非常多的三角面,Unity可以为了性能将它分割成几个独立的子网格。通常这么做是为了符合特定的硬件需求,比如为了减少每个Draw Call中的三角面数量。拆分通常发生在相邻的网格面之间法向角度有较大变化的区域,比如锐角边(Hard Edges)。这样拆分网格的方式会在模型导入流程中执行,在这个过程中,UV Shell也可能会被拆分开来放到不同的光照图,导致生成额外的光照贴图。


Ignore Normals选项可以防止在导入模型时对光照图进行拆分

 

有时通过这种方式对光照图进行拆分并不理想,得到的结果会增加光照图数量并加长预计算的时间,还有可能在照明的接缝处造成不必要的视觉假象。在这种情况下,启用Ignore Normals选项有助于防止光照图在预计算的时候被拆分开来。

 

请注意,这个选项仅对预计算实时光照(PRGI)有影响,物件被拆分的网格仍然会被保留以用于其它用途。


大场景迅速迭代


复杂的场景可能包含着上百或上千的静态对象,计算这些对象的光照图可能会导致预计算变得很慢,对场景的迭代速度产生负面影响。

 

当测试研究上述设置时,将物体放到空场景中会很有帮助,这样可以用最小的预计算时间进行快速迭代。测试好设置之后将该设置复制回原本场景里其它的同类型对象,有助于节省时间。具体操作如下:

  • 打开示例中的LightingTutorialStart场景

  • 从层级视图(Hierarchy)中选择Environment > Structures > Houses下的HouseBig02对象。

  • 按下Ctrl + C键复制该对象(Mac是Cmd + C)

  • 按下Ctrl + N键建立一个新的场景(Mac是Cmd + N)

  • 可能弹出提示框询问是否保存,选择Yes可保存,选择No不保存。

  • 在新的空场景里按下Ctrl + V键粘贴对象(Mac是Cmd+V)

  • 打开Lighting界面(Window > Lighting)选择Scene标签页

  • 确保勾选最下方的Auto

  • 选择Object标签页

  • 在最上方左边的下拉列表中选择Charting

  • 拉大预览视图区域来查看对象UV展开的情况


优化预计算时间


当调整UV展开设置时,最好的结果是找到生成光照图最少与失真度最低的参数组合。 请记住,当启用UV Charts绘制模式时,可以从场景中棋盘图的拉伸状况评估失真程度。



上图中,棋盘图案用来可视化光照贴图的分布,这张图呈现出棋盘图和模型的一致性,代表失真度低。



上图中棋盘图在模型表面被拉伸,表示和光照图的分配不一致。


接下来通过教学示例,来实现我们所学的关于UV展开与优化预计算时间的方法:

  • 打开示例中的LightingTutorialStart场景

  • 从层级视图(Hierarchy)中选择HouseBig02对象(Environment > Structures > Houses下)

  • 打开Lighting界面(Window > Lighting)并选择Object标签页

  • 从上方左边的下拉列表中选择Charting

  • 展开预览视图,注意这里显示了UV Shell与对应颜色的光照图数量


用Charting模式查看预设设置下的HouseBig02对象,还有很大的优化空间

 

HouseBig02是场景里较为复杂的对象,使用了多张光照图,但还可以调整设置来减少光照图的数量。

 

UV展开设置面板

 

不存在某个固定设置能适用于所有UV展开的情况,Unity的UV展开算法会基于预设值进行适当的决定,但通过一些经验积累,可以微调出更好的效果。

 

接下来会开始进行预计算,如果将Auto UV Max Distance调整为0.1,经过短暂的计算时间后,我们可以从预览视图看到光照图数量明显增加许多,检查场景视图可以看到贴图只有轻微拉伸,这是好现象。但我们无法接受光照图数量增加过多。 因为光照图过多代表预计算时间会变长,性能也会降低。

 

但是若Auto UV Max Distance调整为10,现在问题应该会反过来,光照图的数量会减少,但可以从UV Charts绘制模式中看出,光照图拉伸的问题变得很严重。


Auto UV Max Distance值太高可能会导致UV图被拉伸

 

Unity会尝试将UV Shells合并以最小化光照图的数量,当两个Shell要合并时会把这个值一并列入考虑,如果两个Shell的距离大于这个值,就不会被合并。

 

所以当调低Auto UV Max Distance值时,被合并的UV Shell就会减少。这代表最后会生成很多单独的光照图。 而当调高这个值时,距离很远的UV Shell都会被考虑合并,就会生成较少的光照图,但相对而言也会让光照图容易产生扭曲。

 

对于大物体,有时可能需要调高这个值来确保覆盖整个网格面积,相反,如果是小物体,也可以调低这个值来产生更好的效果。以示例HouseBig02来说,将Auto UV Max Distance设为0.8可以达到较好的平衡。


Auto UV Max Distance设为0.8后效果不错。但还可以继续改善

 

Auto UV Max Angle参数和Auto UV Max Distance一样影响着Unity的UV展开算法的结果,该算法除了会检测距离之外,也会检测相邻网格之间的角度,角度低于这个值才会被合并。角度越小表示检测通过率越低,被合并的UV Shell越少,也就会生成更多的光照图。相反,角度越大表示算法能容忍的角度越大,被合并的UV Shell越多。这和Auto UV Max Distance的情况一样,结果是会减少光照图的数量,但是会出现变形的问题。

 

如果将Auto UV Max Angle设置为0,从预览视图可以看到所产生的光照图非常清晰,那是因为我们只允许0或更小的角度才考虑合并,这表示没有UV Shell会被合并,每个都会独立生成光照图。


设置角度过小会导致生成过多的光照图

 

接下来试试将角度调大:

  • 从层级视图(Hierarchy)中选择HouseBig02对象

  • 打开Lighting界面(Window > Lighting)选择Object标签页

  • 慢慢调高Auto UV Max Angle,观察预览视图中的效果

 

可想而知,光照图的数量会随着角度增加而减少,这是因为更多的UV Shell被合并,UV Shell数量降低光照图就会变少。


加大角度有助于减少光照图,尤其是在具有圆形表面的物体上

 

以本例的HouseBig02来说,将Auto UV Max Angle设为93效果最好。请注意预计算完成后减少的光照图数量。


从Charting预览看到优化之后的HouseBig02

 

到这里就可以将测试出来的设置带回原本的场景,请注意,步骤有点复杂:

  • 再次从层级视图(Hierarchy)中选择HouseBig02对象

  • 找到检视面板(Inspector)中的Mesh Renderer组件,点击上面的齿轮按钮后选择Copy Component。

  • 重新打开LightingTutorialStart场景

  • 从层级视图(Hierarchy)中找到原先的HouseBig02的目录

  • 按住Ctrl(Mac按住Cmd)键,然后将所有的HouseBig02一一点选。

  • 打开Lighting界面(Window > Lighting)选择Object标签页

  • 点击齿轮,选择Paste Component Values,刚刚调好的设置就会更新到全部选中的HouseBig02对象上


 

如上图所示,Copy Component是复制设置到另外一个场景较为简便的方法,当必须调整场景中好几百个对象的设置时,这样的操作更能节省时间,将时间花在其它优化上。

 

UV展开设置应该考虑实例(pre-instance)还是预制件(pre-Prefab)呢? 可以将UV展开设置保存在预制件(Prefab)里,只要将这些带有Static Mesh Renderer组件的对象存成预制件即可,或是直接将设置赋给场景中的对象。如果将UV展开设置赋给一个由Prefab实例化(Instance)的对象,那将会覆盖掉原本在Prefab中的设置。这是一种在某些情况下能提供设置场景光照的实用做法。

 

为预制件设置一个默认的UV展开和光照设置通常很有用,如果Prefab需要经常被实例化,这么做就能预先设置对象的UV展开,以节省在场景中进行重复设置的时间。

 

另一方面,这种做法适合一些特定场景的光照设置,在这种场景中的物体会受周遭环境不同程度的影响。例如,在离玩家近的区域内看到的预制件可以设置更高的品质,反之,如果物体离得很远,那就不必使用同样的设置,这里应该降低光照贴图品质,并降低光照图的数量。

 

可以配置一个符合大部分情况的设置,然后在需要的情况下去覆盖这些设置。而本文中,我们是针对每个实例对象进行设定。


整个场景的UV展开


现在是时候学以致用了,就以示例场景为例。在显著改善了预计算时间后,设置场景应该用不了太久。操作步骤如下:

  • 打开LightingTutorialStart场景

  • 从层级视图(Hierarchy)中找到Environment > Structures最上方的对象

  • 将该对象复制到一个空场景

 

到这里应该利用所学内容,尝试各种不同的值,善用UV Charts绘制模式和Charting模式做对比,找到平衡较好的设置。

 

如果不知道如何设置,LightingTutorialOptimal这个示例场景可以作为一个很好的参考,虽然大多数情况下使用预设值就可以得到很好的效果。

  • 尝试使用不同的值来找出光照图数量最低和失真度最小的平衡点

  • 复制设置,回到LightTutorialStart场景将设置赋给所有相同对象。 选择多个对象可以一次粘贴所有设置

  • 对层级视图(Hierarchy)里Structures目录下所有单独命名的对象重复此步骤


一旦所有在Structures目录下的物件都有了优化设置,就可以转到Rocks目录下处理剩下的静态对象了。以这种方式手动循环设置好场景中所有的静态对象,可以确保更好的迭代流程和更佳的执行效能。


总结


本文我们围绕UV展开的概念,对它的参数、光照图可视化和针对大场景的迭代等进行探讨,并且结合教学实例帮助大家理解。觉得收获不小吧?如果想要了解更多的技术文章,请访问Unity官方中文社区(forum.china.unity3d.com)。


更多Unity技术文章

《ReCore》中的全局光照

详解Unity 5 全局光照系统Enlighten问题(上)

详解Unity 5 全局光照系统Enlighten问题(下)

一起来感受Unity 5的光照魔力

Unity 5 中的全局光照技术详解!(建议收藏)




点击“阅读原文”访问Unity官方中文社区!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存